package com.sky.es.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.sky.es.bean.NewsIndexTypeBean;
import com.sky.es.bean.PersonalIndexTypeBean;
import com.sky.es.constants.EsIndexConstants;
import com.sky.es.dto.CommentDTO;
import com.sky.es.dto.Mapping;
import com.sky.es.service.IElasticsearchService;
import com.sky.es.util.ElasticsearchUtil;
import com.sky.es.util.PageResultDto;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.sort.*;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;


@Service
public class ElasticsearchServiceImpl implements IElasticsearchService {

    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
    /*@Resource
    private MyElasticsearchRepository myElasticsearchRepository;*/


    public void createElasticsearchIndex(Class clazz) {
        // 创建索引，会根据Item类的@Document注解信息来创建
        elasticsearchRestTemplate.createIndex(clazz);
        // 配置映射，会根据Item类中的id、Field等字段来自动完成映射
        elasticsearchRestTemplate.putMapping(clazz);
    }

    @Override
    public void insert() {
        Integer id = 10000;
        RestHighLevelClient client = elasticsearchRestTemplate.getClient();
        // 判断索引是否存在
        if (!ElasticsearchUtil.isExistsIndex(client, "esindex")) {
            ElasticsearchUtil.createIndexAndMapping(elasticsearchRestTemplate, PersonalIndexTypeBean.class);
        }
        for (int i = 0; i < 100; i++){
            JSONObject json = new JSONObject();
            json.put("id", id+i);
            json.put("name", "测试姓名" + i + "号");
            json.put("title", "测试标题" + i);
            json.put("content", "测试内容" + i);
            try {
                ElasticsearchUtil.addEsIndex(client, EsIndexConstants.INDEX_NAME, "id", json.toJSONString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void batchInsert() {
        Integer id = 10000;
        List<String> list = new ArrayList<>();
        RestHighLevelClient client = elasticsearchRestTemplate.getClient();
        // 判断索引是否存在
        if (!ElasticsearchUtil.isExistsIndex(client, "esindex")) {
            ElasticsearchUtil.createIndexAndMapping(elasticsearchRestTemplate, PersonalIndexTypeBean.class);
        }
        for (int i = 0; i < 10000; i++){
            JSONObject json = new JSONObject();
            json.put("id", id+i);
            json.put("name", "测试姓名" + i + "号");
            json.put("title", "测试标题" + i);
            json.put("content", "测试内容" + i);
            json.put("date", DateUtil.date());
            list.add(json.toJSONString());
        }

        try {
            ElasticsearchUtil.batchCreateIndex(client, EsIndexConstants.INDEX_NAME, "id", list);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void insertNew() {
        Integer id = 10000;
        RestHighLevelClient client = elasticsearchRestTemplate.getClient();
        // 判断索引是否存在
        if (!ElasticsearchUtil.isExistsIndex(client, EsIndexConstants.NEWS_INDEX_NAME)) {
            ElasticsearchUtil.createIndexAndMapping(elasticsearchRestTemplate, NewsIndexTypeBean.class);
        }
        for (int i = 0; i < 100; i++){
            JSONObject json = new JSONObject();
            json.put("id", id+i);
            json.put("title", "测试新闻标题" + i + "号");
            json.put("author", "测试新闻作者" + i);
            json.put("content", "测试新闻内容" + i);
            json.put("createDate", DateUtil.date());
            List<CommentDTO> comments = new ArrayList<>();
            for (int j = 0; j < 10; j++) {
                CommentDTO dto = new CommentDTO();
                dto.setCommentId("评论ID："+j);
                dto.setCommentAuthor("评论人："+j);
                dto.setCommentContent("评论内容："+j);
                comments.add(dto);
            }
            json.put("comments", comments);
            try {
                ElasticsearchUtil.addEsIndex(client, EsIndexConstants.NEWS_INDEX_NAME, "id", json.toJSONString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public PageResultDto<List<PersonalIndexTypeBean>> findPageResult(Integer start, Integer end, Integer page, Integer size) {
        PageResultDto<List<PersonalIndexTypeBean>> pageResult = new PageResultDto<>();
        BoolQueryBuilder builder = QueryBuilders.boolQuery()
                // 区间查询 gte表示大于等于 lte表示小于等于
                .must(QueryBuilders.rangeQuery("id").gte(start).lte(end));
        RestHighLevelClient client = elasticsearchRestTemplate.getClient();
        SearchHits hits = ElasticsearchUtil.searchByQuery(client, EsIndexConstants.INDEX_NAME, builder, page-1, size, "id", SortOrder.DESC);
        if (hits == null || hits.getHits().length < 1) {
            System.out.println("查询失败，暂无数据");
            return pageResult.failure("查询失败");
        }
        List<PersonalIndexTypeBean> list = new ArrayList<>();
        for (int i = 0; i < hits.getHits().length; i++) {
            Map<String, Object> source = hits.getAt(i).getSourceAsMap();
            PersonalIndexTypeBean bean = new PersonalIndexTypeBean();
            bean.setId(((Number) source.get("id")).intValue());
            bean.setName((String) source.get("name"));
            bean.setContent((String) source.get("content"));
            bean.setTitle((String) source.get("title"));
            bean.setDate(DateUtil.date(((Number) source.get("date")).longValue()));
            list.add(bean);
        }
        pageResult = new PageResultDto<>(size, page, (int) hits.getTotalHits().value, list);
        return pageResult.success();
    }

    @Override
    public PersonalIndexTypeBean findById(Integer id) {
        Optional<PersonalIndexTypeBean> optional = null;//myElasticsearchRepository.findById(id);
        PersonalIndexTypeBean bean = optional.get();
        return bean;
    }

    @Override
    public String findById(String indexName, String id) {
        try {
            return ElasticsearchUtil.findById(elasticsearchRestTemplate.getClient(), indexName, id);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public boolean updateIndexByQuery(String indexName, String fieldName, String value) {
        Script script = new Script(
                ScriptType.INLINE, "painless",
                "ctx._source.age = '30';  ctx._source.description = '条件修改'; ctx._source.team = '条件修改team'",
                Collections.emptyMap());
        BoolQueryBuilder builder = QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery(fieldName, value));
        try {
            return ElasticsearchUtil.updateIndexByQuery(elasticsearchRestTemplate.getClient(), indexName, builder, script);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean deleteIndexByQuery(String indexName, String fieldName, String value) {
        try {
            BoolQueryBuilder builder = QueryBuilders.boolQuery()
                    .must(QueryBuilders.termQuery(fieldName, value))
                    .must(QueryBuilders.termQuery("name", "姓名：name9"));
            return ElasticsearchUtil.deleteIndexByQuery(elasticsearchRestTemplate.getClient(), indexName, builder);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public List<NewsIndexTypeBean> findAll(String indexName) {
        PageResultDto<List<NewsIndexTypeBean>> pageResult = new PageResultDto<>();
        BoolQueryBuilder builder = QueryBuilders.boolQuery()
                .must(QueryBuilders.matchAllQuery());
        RestHighLevelClient client = elasticsearchRestTemplate.getClient();
        SearchHits hits = ElasticsearchUtil.searchByQuery(client, indexName, builder, 0, 2000, "id", SortOrder.DESC);
        if (hits == null || hits.getHits().length < 1) {
            System.out.println("查询失败，暂无数据");
            }
        List<NewsIndexTypeBean> list = new ArrayList<>();
        for (int i = 0; i < hits.getHits().length; i++) {
            Map<String, Object> source = hits.getAt(i).getSourceAsMap();
            NewsIndexTypeBean news = new NewsIndexTypeBean();
            news.setId(((Number) source.get("id")).intValue());
            news.setAuthor((String) source.get("author"));
            news.setContent((String) source.get("content"));
            news.setTitle((String) source.get("title"));
            news.setCreateDate(DateUtil.date(((Number) source.get("createDate")).longValue()));
            news.setComments((List<CommentDTO>) source.get("comments"));
            list.add(news);
        }
        pageResult = new PageResultDto<>(2000, 1, (int) hits.getTotalHits().value, list);
        return pageResult.getBodyData();
    }

    @Override
    public Page<PersonalIndexTypeBean> findByBlogIdPage(String title, String key, Integer start, Integer end, Integer page, Integer size) {
        BoolQueryBuilder builder = QueryBuilders.boolQuery()
                // 区间查询 gte表示大于等于 lte表示小于等于
                .must(QueryBuilders.rangeQuery("id").gte(start).lte(end))
                // 模糊查询 "*" 星号与数据库中模糊查询的百分号相同"%"
                .must(QueryBuilders.wildcardQuery("content", "*" + key + "*"))
                // 完全匹配
                .mustNot(QueryBuilders.termQuery("title", title));
        //组合查询时候must代表and(等于)  mustNot 表示 Not(不等于)  should表示OR(或者)
        // 排序
        FieldSortBuilder fsb = SortBuilders.fieldSort("id").order(SortOrder.DESC);
        SearchQuery searchQuery = new NativeSearchQuery(builder);
        // elasticsearch的初始页是从0开始
        Pageable pageable = PageRequest.of(page-1, size);
        SearchQuery query = new NativeSearchQueryBuilder()
                .withQuery(builder)
                .withSort(fsb)
                .withPageable(pageable)
                .build();
        searchQuery.setPageable(pageable);
        // 深度查询分页
        Page<PersonalIndexTypeBean> result = null;//myElasticsearchRepository.search(query);
        return result;
    }

    @Override
    public List<PersonalIndexTypeBean> findByKey(String title, Long blogId, String summary) {
        BoolQueryBuilder builder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("title", title))
                .must(QueryBuilders.termQuery("id", blogId))
                .must(QueryBuilders.termQuery("summary", summary));
        Iterable<PersonalIndexTypeBean> iterable = null;//myElasticsearchRepository.search(builder);
        return Lists.newArrayList(iterable);
    }

    @Override
    public boolean deleteIndex() {
        RestHighLevelClient client = elasticsearchRestTemplate.getClient();
        //myElasticsearchRepository.deleteById("10079");
        try {
            return ElasticsearchUtil.deleteIndex(client, EsIndexConstants.INDEX_NAME, "10079");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean updateIndex() {
        RestHighLevelClient client = elasticsearchRestTemplate.getClient();
        JSONObject json = new JSONObject();
        json.put("id", 10080);
        json.put("name", "测试姓名1080号");
        json.put("title", "测试标题1080");
        json.put("content", "测试内容1080");
        try {
            return ElasticsearchUtil.updateIndex(client, EsIndexConstants.INDEX_NAME, "id", json.toJSONString());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public Long getCount(String indexName) {
        RestHighLevelClient client = elasticsearchRestTemplate.getClient();
        try {
            return ElasticsearchUtil.getCount(client, indexName);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0L;
    }

    @Override
    public void createIndexAndMapping() {
        RestHighLevelClient client = elasticsearchRestTemplate.getClient();
        try {
            ElasticsearchUtil.createIndex(client, EsIndexConstants.TEST_INDEX_NAME, Mapping.getMapping());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void batchCreateIndex() {
        RestHighLevelClient client = elasticsearchRestTemplate.getClient();
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 2000; i++) {
            JSONObject json = new JSONObject();
            json.put("id", i);
            json.put("name", "姓名：name"+i);
            json.put("age", 28);
            json.put("salary", 5000+i);
            json.put("team", "研发"+i+"小组");
            json.put("position", "深圳市龙华新区民治大道嘉熙业广场"+i);
            json.put("description", "首先要做的是从招聘广告中提炼出关键信息。有些求职者在应聘时将招聘广告上的工作职责忽略，只是自认为达到了要求就投出了通用简历，而招聘单位根本无从考证应聘者是否符合自己的要求、胜任工作。简历中要突出自己相关的工作经历，如果没有相关的工作经历，也可突出相近的工作经历。如果是缺乏工作经验的毕业生，则可尝试介绍与工作内容相符的个人素质，在校期间的活动组织、参与情况，表现自己适合对方的企业文化。另外，建议投简历时附上一份求职信，描述自己对工作的胜任。\n" +
                    "　　不要将自己全部的经历铺陈于HR的面前。对于HR来说，无关的经历能够引起他们兴趣的可能性很小，甚至太多的无关经历还会让求职者“扣分”。\n" +
                    "　　也许你在进入美容美发行业以前，还有其他的工作经验，但是在填写简历的过程中，并不是都需要将其罗列。你只需要描述与自己现 在应聘职位要求所相关的经验、经历就可以了。用这些经验证明你有能力做好自己的目标工作，让用人单位相信你能胜任目标岗位。");
            json.put("addr", "地址：深圳市龙华新区民治大道嘉熙业广场"+i);
            list.add(json.toJSONString());
        }
        try {
            ElasticsearchUtil.batchCreateIndex(client, EsIndexConstants.TEST_INDEX_NAME, "id", list);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
